home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / misc / biology / treedraw.sit / Tree Draw Deck / card_9880.txt < prev    next >
Encoding:
Text File  |  1990-06-26  |  26.0 KB  |  969 lines

  1. -- card: 9880 from stack: in
  2. -- bmap block id: 0
  3. -- flags: 0000
  4. -- background id: 9703
  5. -- name: PlotGramXCMD.p
  6.  
  7.  
  8. -- part contents for background part 3
  9. ----- text -----
  10. DrawGramXCMD.p
  11.  
  12. -- part contents for background part 2
  13. ----- text -----
  14. (* drawgramXCMD.p
  15.  *
  16.  * Mac Hypercard XCMD
  17.  * Usage:
  18.  *     drawgram tree, [parameters,] [output field]
  19.  *       returns HP drawing in The Result
  20.  *
  21.  *       tree == container w/ tree description
  22.  *       param. == cont. w/ parameters
  23.  *       output fld == bkgnd field for text messages, if desired
  24.  *
  25.  * hacked by d.g.gilbert from DrawGram.pas of Phylip package
  26.  * dogStar Software && Indiana University Biology Dept.
  27.  * email: gilbertd@iubio.bio.indiana.edu
  28.  *
  29.  * Language: MPW-Pascal 3.0
  30.  *
  31.  
  32.  #-------- Makefile
  33.   pascal drawgramXCMD.p -mbg off
  34.   Link -w -rt XCMD=10041 Γêé
  35.     -m ENTRYPOINT Γêé
  36.     -sg drawgram Γêé
  37.     drawgramXCMD.p.o Γêé
  38.     "{Libraries}HyperXLib.o" Γêé
  39.     "{Libraries}Interface.o" Γêé
  40.     "{PLibraries}PasLib.o" Γêé
  41.     "{PLibraries}SANElib.o" Γêé
  42.     "{Libraries}Runtime.o" Γêé
  43.     -o "Tree Draw Deck"
  44.  #------------------------
  45.  *
  46.  *
  47.  *)
  48.  
  49.  
  50. {$R-}
  51. {$Z+} (* has to have this or else put EntryPoint in Interface section *)
  52.  
  53. UNIT DummyUnit;
  54.  
  55.  
  56. INTERFACE
  57.  
  58. USES
  59.   Types,
  60.   SANE,
  61.   QuickDraw,
  62.   Memory,
  63.   OSUtils,
  64.   ToolUtils,
  65.   Packages,
  66.   PasLibIntf,
  67.   HyperXCmd;
  68.  
  69.  
  70. IMPLEMENTATION
  71.  
  72.  
  73. (* XCMD entry -- MUST BE FIRST CODE IN SEG *)
  74.  
  75. procedure drawgram(outfile, treefile, parmfile, plotfile: ptr);
  76.   forward;
  77.  
  78. PROCEDURE EntryPoint( pXCmd: XCmdPtr);
  79. CONST
  80.     maxOut  = 2000;
  81.     maxPlot = 70000; {?? this should be expandable, but we'll be lazy}
  82.     Times   = 20; {Font unit}
  83. VAR
  84.     hTree, hParams, hOut, hPlot: Handle;
  85.     outfld  : str255;
  86.     wind    : grafPtr; sfont, ssize : integer;
  87.  
  88. BEGIN
  89.  
  90.   hTree := pXCmd^.params[1];
  91.   if hTree = NIL then begin
  92.     pXCmd^.returnValue := pasToZero(pXcmd,
  93.             '┬º┬º┬º Tree description is missing ');
  94.     exit( EntryPoint);
  95.     end;
  96.   HLock(hTree);
  97.  
  98.   hParams:= pXCmd^.params[2];
  99.   if (hParams = NIL) then hParams:= NewHandleClear(10);
  100.   HLock(hParams);
  101.  
  102.   hOut  := pXCmd^.params[3];
  103.   if hOut = NIL then outfld:= ''
  104.   else zerotopas( pXCmd, hout^, outfld);
  105.   hOut  := NewHandleClear(maxOut);
  106.   HLock(hOut);
  107.  
  108.   hPlot := NewHandleClear(maxPlot);
  109.   HLock(hPlot);
  110.  
  111.   getPort(wind);
  112.   sfont:= wind^.txFont; ssize:= wind^.txSize;
  113.   TextFont( Times); TextSize( 12); {set for label size calcs}
  114.  
  115.   DrawGram(hOut^, hTree^, hParams^, hPlot^);
  116.  
  117.   HUnlock(hPlot);
  118.   HUnlock(hOut);
  119.   HUnlock(hParams);
  120.   HUnlock(hTree);
  121.   TextFont( sfont); TextSize( ssize);
  122.  
  123.   if outfld <> '' then SetFieldByName( pXCmd, false, outfld, hOut);
  124.   DisposHandle( hOut);
  125.  
  126.   pXCmd^.returnValue:= hPlot;
  127. END;
  128.  
  129.  
  130. function min(a,b:longint):longint;
  131. begin
  132.   if (a<b) then min:= a else min:= b;
  133. end;
  134.  
  135. function max(a,b:longint):longint;
  136. begin
  137.   if (a>b) then max:= a else max:= b;
  138. end;
  139.  
  140.  
  141. FUNCTION Strtod(s: str255; var ends: integer): real;
  142. (* stdlib strtod, strtol use %GlobalData !!  *)
  143. var
  144.   x   : real;
  145.   drec: decimal;
  146.   valid: boolean;
  147. BEGIN
  148.   {- ends := 1; -- let caller set this.  1 is lowest for pstr}
  149.   if ends < 1 then ends:= 1;
  150.   str2dec( s, ends, drec, valid);
  151.   StrToD := dec2num( drec);
  152. END;
  153.  
  154.  
  155. { hypercard won't allow globals in XCmds that are needed 
  156.   by standard file handlers }
  157.  
  158. type  carray = packed array [0..32000] of char;
  159.       cp  = ^carray;
  160.  
  161. function hcEof( f: ptr): boolean;
  162. begin
  163.   hcEof:= cp(f)^[0] = chr(0);
  164. end; {hcEof}
  165.  
  166. function  hcEoln( f: ptr): boolean;
  167. begin
  168.   hcEoln:= (cp(f)^[0] = chr(0))
  169.         or (cp(f)^[0] = chr(13));
  170. end; {hcEoln}
  171.  
  172.  
  173. procedure hcRead( var f: ptr; var c: char);
  174. begin
  175.   c:= cp(f)^[0];
  176.   if c <> chr(0) then longint(f):= longint(f) + 1;
  177. end; {hcRead}
  178.  
  179. procedure hcReadReal( var f: ptr; var x: real);
  180. var
  181.   i, e : integer;
  182.   s   : str255;
  183. begin
  184.  i:= 0;
  185.  while (cp(f)^[i] >= ' ') and (i < 255) do begin
  186.    s[i+1]:= cp(f)^[i];
  187.    i:= i+1;
  188.    end;
  189.  s[0]:= chr(i);
  190.  e := 1;
  191.  x := Strtod( s, e);
  192.  longint(f):= longint(f) + e;
  193. end; {hcReadReal}
  194.  
  195. procedure hcReadInt( var f: ptr; var i: integer);
  196. var  x: real;
  197. begin
  198.   hcReadReal(f, x);
  199.   i:= trunc(x);
  200. end; {hcReadInt}
  201.  
  202. procedure hcReadLn( var f: ptr);
  203. begin
  204.   while (cp(f)^[0]<> chr(13))
  205.     and (cp(f)^[0]<> chr( 0)) do
  206.       longint(f):= longint(f) + 1;
  207.   if (cp(f)^[0] = chr(13)) then
  208.       longint(f):= longint(f) + 1;
  209. end; {hcReadLn}
  210.  
  211. procedure hcWrite( var f: ptr; s: str255);
  212. var  j, l: longint;
  213. begin
  214.   l:= length(s);
  215.   for j:= 1 to l do cp(f)^[j-1] := s[j];
  216.   cp(f)^[l+1]:= chr(0);
  217.   longint(f):= longint(f) + l;
  218. end; {hcWrite}
  219.  
  220. procedure hcWriteInt( var f: ptr; n: integer);
  221. var  s: str255;
  222. begin
  223.   numtostring( n, s);
  224.   hcWrite( f, s);
  225. end;
  226.  
  227. procedure hcWriteReal( var f: ptr; x: real; w,d: integer);
  228. var  s  : decstr;
  229.      decfm: decform;
  230. begin
  231.    decfm.style := fixedDecimal;
  232.    decfm.digits:= d;
  233.    Num2Str(decfm, x, s);
  234.    while length(s) < w do s:= concat(' ',s);
  235.    hcWrite( f, str255(s));
  236. end; {hcWriteReal}
  237.  
  238. procedure hcWriteln( var f: ptr; s: str255);
  239. var  s1: string[1];
  240.      outh : handle;
  241.      lout : longint;
  242. begin
  243.   s1 := '1'; s1[1]:= chr(13);
  244.   hcWrite(f, concat(s, s1));
  245. end; {hcWriteln}
  246.  
  247.  
  248. function QDfontHeight: integer;
  249. var  fi: fontInfo;
  250. begin
  251.   getFontInfo(fi);
  252.   QDfontHeight:= fi.ascent + fi.descent + fi.leading;
  253. end;
  254.  
  255.  
  256.  
  257.  
  258.  
  259. procedure drawgram(outfile, treefile, parmfile, plotfile: ptr);
  260.   (* Version 3.3.  Copyright (c) 1986, 1990 by Joseph Felsenstein and
  261.     Christopher A. Meacham.
  262.     Permission is granted to copy, distribute, and modify this
  263.     program provided that (1) this copyright message is not removed
  264.     and (2) no fee is charged for this program.
  265.  
  266.     hacked w/ a machet├⌐ by d.gilbert, June 1990:
  267.     -- dropped a plotters but hp
  268.     -- replaced NEW with NewPtr and DisposeTree
  269.     -- dropped all font stuff
  270.     -- replaced all file i/o with pointer i/o
  271.     -- dropped all interactive stuff
  272.        and replaced w/ parmfile reader
  273.     -- parmfile format:
  274.       -- 1 space b/n param name_value, uppercase is minimum abbrev.
  275.       Grow Horizontal/Vertical
  276.       Style Cladogram/Phenogram/Eurogram/Swoopogram/curVogram
  277.       Uselengths Yes/No
  278.       Rotate 95.0
  279.       Depth 1.23
  280.       Length 1.23
  281.       Nodelength 1.23
  282.       Position Intermediate/Weighted/Centered/iNner/Vshaped
  283.  
  284.     *)
  285.  
  286.   CONST
  287.     maxnodes = 300;
  288.     maxnch = 30;
  289.     point = '.';
  290.     pi = 3.141592653;
  291.     epsilon = 0.00001;
  292.  
  293.     kGrows  = 'G';      {parameter keys, for parmfile}
  294.     kStyle  = 'S';
  295.     kUseLengths = 'U';
  296.     kLabelRot = 'R';
  297.     kTreeDepth = 'D';
  298.     kStemLength = 'L';
  299.     kNodeSpace = 'N';
  300.     kNodePosition = 'P';
  301.  
  302.     HersheyfontHeight = 28;
  303.  
  304.   TYPE
  305.     growth = (vertical, horizontal);
  306.     treestyle = (cladogram, phenogram, curvogram, eurogram, swoopogram);
  307.     penstatustype = (penup, pendown);
  308.     plotstring = packed ARRAY[1..maxnch] OF CHAR;
  309.     nodeptr = ^node;
  310.     node = RECORD
  311.       next, back : nodeptr;
  312.       tip : BOOLEAN;
  313.       nayme : plotstring;
  314.       naymlength, tipsabove : INTEGER;
  315.       xcoord, ycoord, level : REAL
  316.       END;
  317.  
  318.   VAR
  319.     ntips, nextnode, nextnext,
  320.     numtochange, oldx, oldy, nmoves, payge : INTEGER;
  321.     haslengths, uselengths : BOOLEAN;
  322.     xmargin, ymargin, topoflabels, rightoflabels, leftoflabels, tipspacing,
  323.       scale, xscale, yscale, xoffset, yoffset, nodespace, stemlength,
  324.       treedepth, xnow, ynow, xunitspercm, yunitspercm, xsize, ysize, xcorner,
  325.       ycorner, oldxhigh, oldyhigh, oldylow, oldxlow, treeline, labelline,
  326.       labelheight, labelrotation,  expand, rooty : REAL;
  327.     grows : growth;
  328.     style : treestyle;
  329.     root : nodeptr;
  330.     nodep, nextp : ARRAY[1..maxnodes] OF nodeptr;
  331.     penchange, oldpenchange : (yes, no);
  332.     nodeposition : (weighted, intermediate, centered, inner, vshaped);
  333.  
  334.  
  335.   PROCEDURE uppercase (VAR ch : CHAR);
  336.   BEGIN
  337.     IF ((ch >= 'a')  AND (ch <= 'z'))
  338.     THEN ch := CHR(ORD(ch) + ORD('A') - ORD('a'));
  339.   END; (* uppercase *)
  340.  
  341.   PROCEDURE treeread;
  342.     (* read a tree from the treefile and set up nodes and pointers *)
  343.     VAR ch : CHAR;
  344.  
  345.     PROCEDURE getch (VAR c : CHAR);
  346.       (* get next nonblank character *)
  347.     BEGIN
  348.       REPEAT
  349.         IF hcEOLn(treefile) THEN hcReadLN(treefile);
  350.         hcRead(treefile, c);
  351.       UNTIL c <> ' ';
  352.     END; (* getch *)
  353.  
  354.     PROCEDURE addelement (VAR p : nodeptr; q : nodeptr);
  355.       (* read in and add next part of tree, it will be node p
  356.         and will be hooked to pointer q *)
  357.       VAR pfirst : nodeptr;
  358.         n : INTEGER;
  359.         notlast : BOOLEAN;
  360.  
  361.       PROCEDURE processlength(p : nodeptr);
  362.         VAR digit, ordzero : INTEGER;
  363.           valyew, divisor : REAL;
  364.           pointread : BOOLEAN;
  365.       BEGIN (* processlength *)
  366.         ordzero := ORD('0');
  367.         pointread := FALSE;
  368.         valyew := 0.0;
  369.         divisor := 1.0;
  370.         getch(ch);
  371.         digit := ORD(ch)-ordzero;
  372.         WHILE ((digit >= 0) AND (digit <= 9)) OR (ch=point) DO BEGIN
  373.           IF ch = point
  374.           THEN pointread := TRUE
  375.           ELSE BEGIN
  376.             valyew := valyew*10.0 + digit;
  377.             IF pointread
  378.             THEN divisor := divisor*10.0;
  379.             END;
  380.           getch(ch);
  381.           digit := ORD(ch)-ordzero;
  382.           END;
  383.         p^.level := valyew/divisor;
  384.       END; (* processlength *)
  385.  
  386.     BEGIN (* addelement *)
  387.       nextnode := nextnode + 1;
  388.       ptr(p) := NewPtr(sizeof(node)); {-NEW(p);}
  389.       nodep[nextnode] := p;
  390.       IF ch = '(' THEN BEGIN
  391.         p^.tip := FALSE;
  392.         p^.tipsabove := 0;
  393.         pfirst := p;
  394.         notlast := TRUE;
  395.         WHILE notlast DO BEGIN
  396.           ptr(p^.next) := NewPtr(sizeof(node)); {- NEW(p^.next);}
  397.           p := p^.next;
  398.           nextnext:= nextnext + 1;
  399.           nextp[nextnext]:= p;    {!save for dispose}
  400.           p^.tip := FALSE;
  401.           getch (ch);
  402.           addelement (p^.back, p);
  403.           pfirst^.tipsabove := pfirst^.tipsabove + p^.back^.tipsabove;
  404.           IF ch = ')' THEN BEGIN
  405.             notlast := FALSE;
  406.             REPEAT  getch (ch);
  407.             UNTIL (ch = ':') OR (ch = ',') OR (ch = ')') OR (ch = ';');
  408.             END;
  409.           END;
  410.         p^.next := pfirst;
  411.         p := pfirst;
  412.         END
  413.       ELSE BEGIN
  414.         p^.tip := TRUE;
  415.         p^.tipsabove := 1;
  416.         ntips := ntips + 1;
  417.         n := 1;
  418.         REPEAT
  419.           IF (ch = '_') THEN ch := ' ';
  420.           IF n < maxnch
  421.           THEN p^.nayme[n] := ch;
  422.           IF hcEOLn(treefile) THEN hcReadLN(treefile);
  423.           hcRead(treefile, ch);
  424.           n := n + 1;
  425.         UNTIL ((ch = ':') OR (ch = ',') OR (ch = ')'));
  426.         IF n > maxnch
  427.         THEN n := maxnch + 1;
  428.         p^.naymlength := n - 1;
  429.         END;
  430.       IF ch = ':'
  431.       THEN processlength (p)
  432.       ELSE haslengths := haslengths AND (q = NIL);
  433.       p^.back := q;
  434.     END; (* addelement *)
  435.  
  436.   BEGIN (* treeread *)
  437.     haslengths := TRUE;
  438.     ntips := 0;
  439.     nextnode := 0; nextnext:= 0;
  440.     getch (ch);
  441.     addelement (root, NIL);
  442.     hcReadLN(treefile);
  443.     uselengths := haslengths;
  444.   END; (* treeread *)
  445.  
  446.  
  447.   procedure disposeTree;
  448.   { release Ptrs for Mac/Hypercard }
  449.   var  i: integer;
  450.   begin
  451.     for i:= 1 to nextnode do DisposPtr(ptr(nodep[i]));
  452.     for i:= 1 to nextnext do DisposPtr(ptr(nextp[i]));
  453.   end;
  454.  
  455.  
  456.   PROCEDURE plotrparms;
  457.     (* set up initial characteristics of plotter or printer *)
  458.   BEGIN
  459.     xcorner := 0.0;
  460.     ycorner := 0.0;
  461. {Hewlett-Packard plot setup}
  462.     penchange := yes;
  463.     xunitspercm := 400.0;
  464.     yunitspercm := 400.0;
  465.     xsize := 24.0;
  466.     ysize := 18.0;
  467.   END; (* plotrparms *)
  468.  
  469.   PROCEDURE initialparms;
  470.   BEGIN
  471.     plotrparms;
  472.     xmargin := 0.08 * xsize;
  473.     ymargin := 0.08 * ysize;
  474.     xscale := xunitspercm;
  475.     yscale := yunitspercm;
  476.     style := cladogram;
  477.     grows := vertical;
  478.     labelrotation := 45.0;
  479.     nodespace := 3.0;
  480.     stemlength := 0.05;
  481.     treedepth := 0.5/0.95;
  482.     IF uselengths
  483.     THEN nodeposition := intermediate
  484.     ELSE nodeposition := vshaped;
  485.   END; (* initialparms *)
  486.  
  487.   PROCEDURE getparms;
  488.     (* get from user the relevant parameters for the plotter and diagram *)
  489.   VAR key, ch : CHAR;
  490.       ok : BOOLEAN;
  491.       x : real;
  492.   BEGIN
  493.   while not hcEOF(parmfile) do begin
  494.     {read parameter key/name}
  495.     repeat hcRead(parmfile, key); until (key > ' ') or hcEOLn(parmfile);
  496.     {skip rest of parameter name...}
  497.     repeat hcRead(parmfile, ch);  until (ch <= ' ') or hcEOLn(parmfile);
  498.  
  499.     uppercase(key);
  500.     CASE key OF
  501.  
  502.       kGrows: BEGIN  {! note change in documentation }
  503.         hcread(parmfile, ch);
  504.         uppercase(ch);
  505.         case ch of
  506.           'H' : grows := horizontal;
  507.           'V' : grows := vertical;
  508.           end;
  509.         END;
  510.  
  511.       kStyle: BEGIN
  512.         hcread(parmfile, ch);
  513.         uppercase (ch);
  514.         CASE ch OF
  515.           'C' : style := cladogram;
  516.           'P' : style := phenogram;
  517.           'E' : style := eurogram;
  518.           'S' : style := swoopogram;
  519.           'V' : style := curvogram
  520.           END;
  521.         END;
  522.  
  523.       kUseLengths: BEGIN
  524.         hcread(parmfile, ch);
  525.         uppercase (ch);
  526.         if haslengths then CASE ch OF
  527.           'Y': begin uselengths:= true;
  528.                nodeposition:= intermediate;
  529.                end;
  530.           'N': begin uselengths:= false;
  531.                nodeposition:= vshaped;
  532.                end;
  533.             end;
  534.         END;
  535.  
  536.       kLabelRot: BEGIN
  537.         hcREADReal(parmfile, labelrotation);
  538.         END;
  539.  
  540.       kTreeDepth: BEGIN
  541.         hcReadReal(parmfile, treedepth);
  542.         END;
  543.  
  544.       kStemLength: BEGIN
  545.         hcReadReal(parmfile, x);
  546.         if (x >= 0.0) AND (x < 0.9) then stemlength := x;
  547.         END;
  548.  
  549.       kNodeSpace: BEGIN
  550.         hcReadReal(parmfile, x);
  551.         if (x <> 0.0) then nodespace := 1.0/x;
  552.         END;
  553.  
  554.       kNodePosition: BEGIN
  555.         hcRead(parmfile, ch);
  556.         uppercase(ch);
  557.         CASE ch OF
  558.           'W' : nodeposition := weighted;
  559.           'I' : nodeposition := intermediate;
  560.           'C' : nodeposition := centered;
  561.           'N' : nodeposition := inner;
  562.           'V' : nodeposition := vshaped
  563.           END;
  564.         END;
  565.  
  566.       END;
  567.     hcReadln(parmfile);
  568.     end;
  569.   END; (* getparms *)
  570.  
  571.  
  572.  
  573.   PROCEDURE calculate;
  574.     (* compute coordinates for tree *)
  575.     VAR sum, tipx, maxtextlength, textlength,
  576.       firstlet, maxheight, fontheight, angle : REAL;
  577.       i : INTEGER;
  578.  
  579.     PROCEDURE calctraverse (p : nodeptr; lengthsum : REAL);
  580.       (* traverse to establish initial node coordinates *)
  581.       VAR x1, y1, x2, y2, x3, w1, w2, sumwx, sumw, nodeheight : REAL;
  582.         pp, plast : nodeptr;
  583.     BEGIN (* calctraverse *)
  584.       IF p = root
  585.       THEN nodeheight := 0.0
  586.       ELSE IF uselengths
  587.         THEN nodeheight := lengthsum + p^.level
  588.         ELSE nodeheight := 1.0;
  589.       IF nodeheight > maxheight THEN maxheight := nodeheight;
  590.       IF p^.tip
  591.       THEN BEGIN
  592.         p^.xcoord := tipx;
  593.         IF uselengths
  594.         THEN p^.ycoord := nodeheight
  595.         ELSE p^.ycoord := 1.0;
  596.         tipx := tipx + tipspacing;
  597.         END
  598.       ELSE BEGIN
  599.         sumwx := 0.0;
  600.         sumw := 0.0;
  601.         pp := p^.next;
  602.         x3 := 0.0;
  603.         REPEAT
  604.           calctraverse (pp^.back, nodeheight);
  605.           sumw := sumw + pp^.back^.tipsabove;
  606.           sumwx := sumwx + pp^.back^.tipsabove*pp^.back^.xcoord;
  607.           IF ABS(pp^.back^.xcoord-0.5) < ABS(x3-0.5)
  608.           THEN x3 := pp^.back^.xcoord;
  609.           plast := pp;
  610.           pp := pp^.next;
  611.         UNTIL pp = p;
  612.         x1 := p^.next^.back^.xcoord;
  613.         x2 := plast^.back^.xcoord;
  614.         y1 := p^.next^.back^.ycoord;
  615.         y2 := plast^.back^.ycoord;
  616.         CASE nodeposition OF
  617.           weighted : BEGIN
  618.             w1 := y1 - nodeheight;
  619.             w2 := y2 - nodeheight;
  620.             IF (w1 + w2) <= 0.0
  621.             THEN p^.xcoord := (x1 + x2)/2.0
  622.             ELSE p^.xcoord := (w2*x1 + w1*x2)/(w1+w2);
  623.             END;
  624.           intermediate : p^.xcoord := (x1 + x2)/2.0;
  625.           centered : p^.xcoord := sumwx/sumw;
  626.           inner : p^.xcoord := x3;
  627.           vshaped : p^.xcoord := (x1 + x2 + (y1 - y2)/maxheight)/2.0
  628.           END;
  629.         IF uselengths
  630.         THEN p^.ycoord := nodeheight
  631.         ELSE BEGIN
  632.           p^.ycoord := (x1 - x2 + y1 + y2)/2.0;
  633.           IF nodeposition = inner
  634.           THEN BEGIN
  635.             IF ABS(x1-0.5) > ABS(x2 - 0.5)
  636.             THEN BEGIN
  637.               p^.ycoord := y1 + x1 - x2;
  638.               w1 := y2 - p^.ycoord;
  639.               END
  640.             ELSE BEGIN
  641.               p^.ycoord := y2 + x1 - x2;
  642.               w1 := y1 - p^.ycoord;
  643.               END;
  644.             IF w1 < epsilon
  645.             THEN p^.ycoord := p^.ycoord - ABS(x1-x2);
  646.             END;
  647.           END;
  648.         END;
  649.     END; (* traverse *)
  650.  
  651.     FUNCTION lengthtext(pstring : plotstring; nchars : INTEGER) : REAL;
  652.        VAR i, j, code : INTEGER;
  653.           cfix, sumlength, heightfont, widthfont : REAL;
  654.     BEGIN
  655.        sumlength := 0.0;
  656.        heightfont:= HersheyfontHeight;
  657.        cfix:= HersheyfontHeight / QDfontHeight;
  658.        FOR i := 1 TO nchars DO BEGIN
  659.           widthfont:= round( cfix * charwidth(pstring[i]));
  660.           sumlength := sumlength + widthfont;
  661.           END;
  662.        lengthtext := sumlength;
  663.     END; (* lengthtext *)
  664.  
  665.   BEGIN (* calculate *)
  666.     maxheight := 0.0;
  667.     maxtextlength := 0.0;
  668.     IF nodep[1]^.naymlength > 0
  669.     THEN firstlet := lengthtext (nodep[1]^.nayme, 1)
  670.     ELSE firstlet := 0.0;
  671.     sum := 0.0;
  672.     tipx := 0.0;
  673.     FOR i := 1 TO nextnode DO
  674.       IF nodep[i]^.tip
  675.       THEN BEGIN
  676.         textlength := lengthtext (nodep[i]^.nayme, nodep[i]^.naymlength);
  677.         IF textlength > maxtextlength
  678.         THEN maxtextlength := textlength;
  679.         END;
  680.     fontheight := HersheyfontHeight;
  681.     angle := pi*labelrotation/180.0;
  682.     maxtextlength := maxtextlength/fontheight;
  683.     textlength := textlength/fontheight;
  684.     firstlet := firstlet/fontheight;
  685.     IF ntips > 1
  686.     THEN labelheight := 1.0/(nodespace*(ntips-1))
  687.     ELSE labelheight := 1.0/nodespace;
  688.     IF angle < (pi/6.0)
  689.     THEN tipspacing := (nodespace
  690.                   + COS(angle)*(maxtextlength-0.5))*labelheight
  691.     ELSE IF ntips > 1
  692.       THEN tipspacing := 1.0/(ntips-1.0)
  693.       ELSE tipspacing := 1.0;
  694.     topoflabels := labelheight*(1.0 + SIN(angle)*(maxtextlength-0.5)
  695.                           + COS(angle)*0.5);
  696.     rightoflabels := labelheight*(COS(angle)*(textlength-0.5) +SIN(angle)*0.5);
  697.     leftoflabels := labelheight*(COS(angle)*firstlet*0.5+SIN(angle)*0.5);
  698.     calctraverse (root, sum);
  699.     rooty := root^.ycoord;
  700.     FOR i := 1 TO nextnode DO
  701.       nodep[i]^.ycoord := stemlength*treedepth + (1.0-stemlength)*treedepth
  702.          *(nodep[i]^.ycoord-rooty)/(maxheight-rooty);
  703.     rooty := 0.0;
  704.   END; (* calculate *)
  705.  
  706.   PROCEDURE rescale;
  707.     (* compute coordinates of tree for plot or preview device *)
  708.     VAR i : INTEGER;
  709.       treeheight, treewidth, extrax, extray, temp : REAL;
  710.   BEGIN (* rescale *)
  711.     treeheight := 0.0;
  712.     FOR i := 1 TO nextnode DO
  713.       IF nodep[i]^.ycoord > treeheight
  714.       THEN treeheight := nodep[i]^.ycoord;
  715.     treeheight := treeheight + topoflabels;
  716.     treewidth := (ntips-1)*tipspacing + rightoflabels + leftoflabels;
  717.     IF grows = vertical
  718.     THEN BEGIN
  719.       expand := (xsize - 2*xmargin)/treewidth;
  720.       IF (ysize - 2*ymargin)/treeheight < expand
  721.       THEN expand := (ysize - 2*ymargin)/treeheight;
  722.       extrax := (xsize - 2*xmargin - treewidth*expand)/2.0;
  723.       extray := (ysize - 2*ymargin - treeheight*expand)/2.0;
  724.       END
  725.     ELSE BEGIN
  726.       expand := (ysize - 2*ymargin)/treewidth;
  727.       IF (xsize - 2*xmargin)/treeheight < expand
  728.       THEN expand := (xsize - 2*xmargin)/treeheight;
  729.       extrax := (xsize - 2*xmargin - treeheight*expand)/2.0;
  730.       extray := (ysize - 2*ymargin - treewidth*expand)/2.0;
  731.       END;
  732.     FOR i := 1 TO nextnode DO BEGIN
  733.       nodep[i]^.xcoord := expand*(nodep[i]^.xcoord + leftoflabels);
  734.       nodep[i]^.ycoord := expand*(nodep[i]^.ycoord - rooty);
  735.       IF grows = horizontal
  736.       THEN BEGIN
  737.         temp := nodep[i]^.ycoord;
  738.         nodep[i]^.ycoord := expand*treewidth-nodep[i]^.xcoord;
  739.         nodep[i]^.xcoord := temp;
  740.         END;
  741.       nodep[i]^.xcoord := nodep[i]^.xcoord + xmargin + extrax;
  742.       nodep[i]^.ycoord := nodep[i]^.ycoord + ymargin + extray;
  743.       END;
  744.     IF grows = vertical
  745.     THEN rooty := ymargin + extray
  746.     ELSE rooty := xmargin + extrax;
  747.   END; (* rescale *)
  748.  
  749.   PROCEDURE plot(pen : penstatustype; xabs, yabs : REAL); {for HP}
  750.   BEGIN
  751.     IF pen=pendown THEN hcWrite(plotfile, 'PD')
  752.                    ELSE hcWrite(plotfile, 'PU');
  753.     hcWriteInt(plotfile, ROUND(xabs));
  754.     hcWrite(plotfile, ',');
  755.     hcWriteInt(plotfile, ROUND(yabs));
  756.     hcWriteLN(plotfile, ';');
  757.   END; (* plot *)
  758.  
  759.   PROCEDURE plottree (p, q : nodeptr);
  760.     (* plot part or all of tree on the plotting device *)
  761.     CONST segments = 40;
  762.     VAR i : INTEGER;
  763.       x1, y1, x2, y2, x3, y3, f, g, h, fract, minny, miny : REAL;
  764.       pp : nodeptr;
  765.   BEGIN (* plottree *)
  766.     x2 := xscale*(xoffset + p^.xcoord);
  767.     y2 := yscale*(yoffset + p^.ycoord);
  768.     IF p <> root
  769.     THEN BEGIN
  770.       x1 := xscale*(xoffset + q^.xcoord);
  771.       y1 := yscale*(yoffset + q^.ycoord);
  772.       plot (penup, x1, y1);
  773.       CASE style OF
  774.         cladogram : plot (pendown, x2, y2);
  775.         phenogram : BEGIN
  776.           IF grows = vertical
  777.           THEN plot (pendown, x2, y1)
  778.           ELSE plot (pendown, x1, y2);
  779.           plot (pendown, x2, y2);
  780.           END;
  781.         curvogram : FOR i := 1 TO segments DO BEGIN
  782.             f := i/segments;
  783.             g := i/segments;
  784.             h := 1.0 - SQRT(1.0-g*g);
  785.             IF grows = vertical
  786.             THEN BEGIN
  787.               x3 := x1*(1.0-f)+x2*f;
  788.               y3 := y1 + (y2 - y1)*h;
  789.               END
  790.             ELSE BEGIN
  791.               x3 := x1 + (x2 - x1)*h;
  792.               y3 := y1*(1.0-f)+y2*f;
  793.               END;
  794.             plot (pendown, x3, y3);
  795.             END;
  796.         eurogram : BEGIN
  797.           IF grows = vertical
  798.           THEN plot (pendown, x2, (2*y1+y2)/3)
  799.           ELSE plot (pendown, (2*x1+x2)/3, y2);
  800.           plot (pendown, x2, y2);
  801.           END;
  802.         swoopogram : IF (grows = vertical) AND (NOT (ABS(y1-y2) < epsilon))
  803.               OR ((grows = horizontal) AND (NOT (ABS(x1-x2) < epsilon)))
  804.           THEN BEGIN
  805.             IF grows = vertical
  806.             THEN miny := p^.ycoord
  807.             ELSE miny := p^.xcoord;
  808.             pp := q^.next;
  809.             WHILE pp <> q DO BEGIN
  810.               IF grows = vertical
  811.               THEN minny := pp^.back^.ycoord
  812.               ELSE minny := pp^.back^.xcoord;
  813.               IF minny < miny
  814.               THEN miny := minny;
  815.               pp := pp^.next;
  816.               END;
  817.             IF grows = vertical
  818.             THEN miny := yscale*(yoffset+miny)
  819.             ELSE miny := xscale*(xoffset+miny);
  820.             IF grows = vertical
  821.             THEN fract := 0.3333*(miny-y1)/(y2-y1)
  822.             ELSE fract := 0.3333*(miny-x1)/(x2-x1);
  823.             FOR i := 1 TO segments DO BEGIN
  824.               f := i/segments;
  825.               IF f < fract
  826.               THEN g := f/fract
  827.               ELSE g := (f-fract)/(1.0-fract);
  828.               IF f < fract
  829.               THEN h := fract*SQRT(1.0-(1.0-g)*(1.0-g))
  830.               ELSE h := fract + (1.0-fract)*(1.000001 - SQRT(1.000001-g*g));
  831.               IF grows = vertical
  832.               THEN BEGIN
  833.                 x3 := x1*(1.0-f)+x2*f;
  834.                 y3 := y1 + (y2 - y1)*h;
  835.                 END
  836.               ELSE BEGIN
  837.                 x3 := x1 + (x2 - x1)*h;
  838.                 y3 := y1*(1.0-f)+y2*f;
  839.                 END;
  840.               plot (pendown, x3, y3);
  841.               END;
  842.             END
  843.         END;
  844.       END
  845.     ELSE BEGIN
  846.       IF grows = vertical
  847.       THEN BEGIN
  848.         x1 := xscale*(xoffset + p^.xcoord);
  849.         y1 := yscale*(yoffset + rooty);
  850.         END
  851.       ELSE BEGIN
  852.         x1 := xscale*(xoffset + rooty);
  853.         y1 := yscale*(yoffset + p^.ycoord);
  854.         END;
  855.       plot (penup, x1, y1);
  856.       plot (pendown, x2, y2);
  857.       END;
  858.     IF NOT p^.tip
  859.     THEN BEGIN
  860.       pp := p^.next;
  861.       WHILE pp <> p DO BEGIN
  862.         plottree (pp^.back, p);
  863.         pp := pp^.next;
  864.         END;
  865.       END;
  866.   END; (* plottree *)
  867.  
  868.   PROCEDURE plotlabels;
  869.     TYPE pentype = (treepen, labelpen);
  870.     VAR i : INTEGER;
  871.       compr, dx, dy, angle : REAL;
  872.       lp : nodeptr;
  873.  
  874.     PROCEDURE changepen(pen : pentype);
  875.     BEGIN (* changepen *)
  876.       CASE pen OF
  877.         treepen:  hcWriteLN(plotfile, 'SP1;');
  878.         labelpen: hcWriteLN(plotfile, 'SP2;');
  879.         END;
  880.     END; (* changepen *)
  881.  
  882.   PROCEDURE plottext(pstring : plotstring; nchars : INTEGER;
  883.     height, compress,  x, y, slope : REAL);
  884.   CONST xstart = 10;
  885.      ystart = 35;
  886.   VAR i, j : INTEGER;
  887.      sinslope, cosslope : REAL;
  888.      penstatus : penstatustype;
  889.      s1 : string[1];
  890.   BEGIN (* plottext *)
  891.     sinslope := SIN(pi*slope/180.0);
  892.     cosslope := COS(pi*slope/180.0);
  893.     plot( penup, x, y);
  894.     hcWrite(plotfile,'DI ');
  895.     hcWriteReal(plotfile, cosslope,7,4);
  896.     hcWrite(plotfile,', ');
  897.     hcWriteReal(plotfile, sinslope,7,4);
  898.     hcWriteln(plotfile,';');
  899.     hcWrite(plotfile,'LB');
  900.     for i:= 1 to nchars do hcWrite(plotfile,pstring[i]);
  901.     s1:= '1'; s1[1]:= chr(3);
  902.     hcWriteln(plotfile,s1);
  903.   END; (* plottext *)
  904.  
  905.   BEGIN (* plotlabels *)
  906.     compr := xunitspercm/yunitspercm;
  907.     IF penchange = yes THEN changepen(labelpen);
  908.     angle := labelrotation*pi/180.0;
  909.     FOR i := 1 TO nextnode DO BEGIN
  910.       IF nodep[i]^.tip
  911.       THEN BEGIN
  912.         lp := nodep[i];
  913.         dx := -labelheight*expand*0.70710*COS(angle+pi/4.0);
  914.         dy := labelheight*expand*(1.0-0.70710*SIN(angle+pi/4.0));
  915.         IF grows = vertical
  916.         THEN plottext(lp^.nayme,lp^.naymlength,
  917.           labelheight*expand*xscale/compr, compr,
  918.           xscale*(lp^.xcoord+dx+xoffset), yscale*(lp^.ycoord+dy+yoffset),
  919.           -labelrotation)
  920.         ELSE plottext(lp^.nayme,lp^.naymlength,
  921.           labelheight*expand*yscale, compr, xscale*(lp^.xcoord+dy+xoffset),
  922.           yscale*(lp^.ycoord-dx+yoffset), -labelrotation+90.0)
  923.         END;
  924.       END;
  925.     IF penchange = yes THEN changepen(treepen);
  926.   END; (* plotlabels *)
  927.  
  928.   PROCEDURE initplotter;
  929.   BEGIN
  930.     hcWriteLN(plotfile, 'IN;SP1;VS10.0;');
  931.   END;
  932.  
  933.   PROCEDURE finishplotter;
  934.   BEGIN
  935.     plot(penup, 1.0, 1.0);
  936.     hcWriteLN(plotfile,'SP;AF;');
  937.   END;
  938.  
  939.   PROCEDURE drawit;
  940.   BEGIN
  941.     xoffset := 0.0;
  942.     yoffset := 0.0;
  943.     plottree (root, root);
  944.     plotlabels;
  945.   END; (* drawit *)
  946.  
  947.  
  948. BEGIN (* drawgram *)
  949.   hcWriteLN(outfile,'DRAWGRAM from PHYLIP version 3.3');
  950.   hcWriteLN(outfile,'Reading tree ... ');
  951.   treeread;
  952.   hcWritelN(outfile,'Tree has been read.');
  953.   initialparms;
  954.   getparms;
  955.   calculate;
  956.   rescale;
  957.   initplotter;
  958.   hcWriteLN(outfile,'Writing plot file ...');
  959.   drawit;
  960.   finishplotter;
  961.   disposeTree;
  962.   hcWriteLN(outfile,'Finished.');
  963. END; {drawGram}
  964.  
  965. END. {DummyUnit}
  966.  
  967.  
  968.  
  969.